<template>
   <div class="countup-wrap">
    <slot name="prefix"></slot>
    <span ref="elRef"> </span>
    <slot name="suffix"></slot>
  </div>
</template>
<script lang="ts">
export type { CountUp as ICountUp, CountUpOptions } from 'countup.js'
export default {
  name: 'CountUp'
}
</script>

<script setup lang="ts">
import { onMounted, onUnmounted, ref, watch } from 'vue'
import { CountUp } from 'countup.js'
import type { CountUpOptions } from 'countup.js'

const props = withDefaults(
    defineProps<{
        // 结束数值
        endVal:number |string,
        // 开始数值
        startVal:number | string,
        // 动画时长
        duration?:number |string,
        // 是否自动计数
        autoplay:boolean,
        // 循环次数，有限次数/无线循环
        loop?:boolean |number|string,
        // 延时
        delay?:number,
        // countup配置项
        options?:CountUpOptions
    }>(),{
        startVal:0,
        duration:2.5,
        autoplay:true,
        loop:false,
        delay:0,
        options:void 0
    }
)

const emits =defineEmits<{
    // countup init complete ==>  countup 初始化完成
    (event:'init',countup:CountUp):void
    //count complete ==>统计完成
    (event:'finished'):void
}>()

const elRef = ref<HTMLElement>()
let countup = ref<CountUp>()

/**
 * 初始化组件数据
 */
function initCountUp(){
    if(!elRef.value){
        return
    }
   const startVal=Number(props.startVal)
   const duration=Number(props.duration)
   const endVal=Number(props.endVal)
/**
 * 实例化CountUp ==>实例化数字动画插件
 */
   countup.value = new CountUp(elRef.value,endVal,{
    startVal,
    duration,
    ...props.options
   })
   if(countup.value.error){
    console.log(countup.value.error)
    return
   }
   emits('init',countup.value)
}

const startAnim =(cb?:()=>void)=>{
  return  countup.value?.start(cb)
}

watch(()=>props.endVal,(val)=>{
    if(props.autoplay){
        countup.value?.update(val)
    }
})

const finished = ref(false)
let loopCount = 0
const loopAnim =()=>{
    loopCount ++
    startAnim(()=>{
        const isTruely = typeof props.loop === 'boolean' && props.loop
        if(isTruely || props.loop > loopCount){
            delay(()=>{
                countup.value?.reset()
                loopAnim()
            },props.delay)
        }else{
            finished.value =true
        }
    })
}

watch(()=>finished,(flag)=>{
    if(flag){
        emits('finished')
    }
})


let dalayRafId :number
/**
 * 延时函数
 * @param cb 
 * @param seconds 延时时间
 */
const delay =(cb:()=>unknown,seconds =1)=>{
    let startTime :number
    function count(timestamp:number){
        if(!startTime){
            startTime = timestamp
        }
        const diff = timestamp - startTime
        if(diff < seconds * 1000){
            /**更新动画 */
            dalayRafId =requestAnimationFrame(count)
        }else{
            cb()
        }
    }
    dalayRafId =requestAnimationFrame(count)
}


function restart(){
    initCountUp()
    startAnim()
}


onMounted(()=>{
    initCountUp()
    if(props.autoplay){
        loopAnim()
    }
})
onUnmounted(()=>{
    /**取消一个先前通过调用requestAnimationFrame() 方法添加到计划中的动画帧请求 */
    cancelAnimationFrame(dalayRafId)
    countup.value?.reset()
})

/**
 * 向父组件暴露方法和属性
 */
 defineExpose({
    init:initCountUp,
    restart
})


</script>

<style lang="scss" scoped>

</style>